﻿namespace LogManager.Gui.Test.ViewModels
{
    using System;
    using System.ComponentModel;
    using System.Linq;
    using System.Threading.Tasks;
    using LogManager.Core.Infrastructure.Logging;
    using LogManager.Core.Models;
    using LogManager.Core.Services;
    using LogManager.Core.Services.Location;
    using LogManager.Gui.AppStart;
    using LogManager.Gui.ViewModels;
    using LogManager.Gui.Views;
    using LogManager.TestUtility;
    using LogManager.Utils.Unity;
    using Microsoft.Practices.Unity;
    using Microsoft.Practices.Unity.InterceptionExtension;
    using NSubstitute;
    using NUnit.Framework;
    using N = NUnit.Framework;

    [TestFixture]
    public class TestLocationViewModel
    {
        [Test]
        public void LocationViewModel_Ctor_BasicSetup_Ok()
        {
            var view = Substitute.For<ILocationView>();
            var location = Substitute.For<ILocation>();
            var locationService = Substitute.For<ILocationService>();

            var locationViewModel = new LocationViewModel(view, location, locationService);

            Assert.AreEqual(locationViewModel.View, view);
            Assert.AreEqual(locationViewModel.Location, location);
        }

        [Test]
        public void LocationViewModel_DeleteLocationCommand_Execute_Ok()
        {
            var locationViewModel = CreateLocationViewModel();

            locationViewModel.DeleteLocationCommand.Execute(null);
            locationViewModel.Location
                .Received()
                .DeleteLocation();
        }

        [Test]
        public void LocationViewModel_CheckSourceAccessCommand_ExecuteWithSuccessResult_Ok()
        {
            var locationService = Substitute.For<ILocationService>();
            var locationViewModel = CreateLocationViewModel(locationService);
            var successResult = Task.Factory.StartNew<SimpleServiceResult>(() => SimpleServiceResult.CreateSuccess("Ok"));

            locationService.CheckLocationSourceAsync(locationViewModel.Location)
                .Returns(successResult);

            using (var logTrakcker = new LogTracker(Log.Logger))
            {
                // When
                locationViewModel.CheckSourceAccessCommand.ExecuteAsync(null).Wait();

                Assert.AreEqual(1, logTrakcker.Logs.Count);
                Assert.AreEqual(LogType.Success, logTrakcker.Logs.Single().LogType);
            }

            // Assert
            locationService
                  .Received(1)
                  .CheckLocationSourceAsync(locationViewModel.Location);
        }

        [Test]
        public void LocationViewModel_CheckSourceAccessCommand_ExecuteWithFailureResult_Ok()
        {
            var locationService = Substitute.For<ILocationService>();
            var locationViewModel = CreateLocationViewModel(locationService);
            var failResult = Task.Factory.StartNew<SimpleServiceResult>(() => SimpleServiceResult.CreateFailure("bad", new Exception()));
            locationService.CheckLocationSourceAsync(locationViewModel.Location)
                .Returns(failResult);

            using (var logTrakcker = new LogTracker(Log.Logger))
            {
                // When
                locationViewModel.CheckSourceAccessCommand.ExecuteAsync(null).Wait();

                Assert.AreEqual(1, logTrakcker.Logs.Count);
                Assert.AreEqual(LogType.Error, logTrakcker.Logs.Single().LogType);
            }

            // Assert
            locationService
                  .Received(1)
                  .CheckLocationSourceAsync(locationViewModel.Location);
        }

        [Test]
        public void LocationViewModel_CheckDestinationAccessCommand_ExecuteWithSuccessResult_Ok()
        {
            var locationService = Substitute.For<ILocationService>();
            var locationViewModel = CreateLocationViewModel(locationService);
            var successResult = Task.Factory.StartNew<SimpleServiceResult>(() => SimpleServiceResult.CreateSuccess("Ok"));
            locationService.CheckLocationDestinationAsync(locationViewModel.Location)
                .Returns(successResult);

            using (var logTrakcker = new LogTracker(Log.Logger))
            {
                // When
                locationViewModel.CheckDestinationAccessCommand.ExecuteAsync(null).Wait();

                Assert.AreEqual(1, logTrakcker.Logs.Count);
                Assert.AreEqual(LogType.Success, logTrakcker.Logs.Single().LogType);
            }

            // Assert
            locationService
                  .Received(1)
                  .CheckLocationDestinationAsync(locationViewModel.Location);
        }

        [Test]
        public void LocationViewModel_CheckDestinationAccessCommand_ExecuteWithFailureResult_Ok()
        {
            var locationService = Substitute.For<ILocationService>();
            var locationViewModel = CreateLocationViewModel(locationService);
            var failResult = Task.Factory.StartNew<SimpleServiceResult>(() => SimpleServiceResult.CreateFailure("bad", new Exception()));

            locationService.CheckLocationDestinationAsync(locationViewModel.Location)
                .Returns(failResult);

            using (var logTrakcker = new LogTracker(Log.Logger))
            {
                // When
                locationViewModel.CheckDestinationAccessCommand.ExecuteAsync(null).Wait();

                Assert.AreEqual(1, logTrakcker.Logs.Count);
                Assert.AreEqual(LogType.Error, logTrakcker.Logs.Single().LogType);
            }

            // Assert
            locationService
                  .Received(1)
                  .CheckLocationDestinationAsync(locationViewModel.Location);
        }

        [Test]
        public void LocationViewModel_Location_CheckPropertyChangedRaised_ChangeRaised()
        {
            var locationViewModel = CreateInterceptedLocationViewModel();
            locationViewModel.ShouldNotifyOn(p => p.Location).WhenModifing(p => p.Location);
        }

        [Test]
        public void LocationViewModel_SourceCheckResult_CheckPropertyCanRaiseChanged_ChangeRaised()
        {
            var locationViewModel = CreateInterceptedLocationViewModel();
            locationViewModel.ShouldNotifyOn(p => p.SourceCheckResult).WhenModifing(p => p.SourceCheckResult);
        }

        [Test]
        public void LocationViewModel_DestinationCheckResult_CheckPropertyCanRaiseChanged_ChangeRaised()
        {
            var locationViewModel = CreateInterceptedLocationViewModel();
            locationViewModel.ShouldNotifyOn(p => p.DestinationCheckResult).WhenModifing(p => p.DestinationCheckResult);
        }

        [Test]
        public void LocationViewModel_DestinationCheckIsloading_CheckPropertyCanRaiseChanged_ChangeRaised()
        {
            var locationViewModel = CreateInterceptedLocationViewModel();
            locationViewModel.ShouldNotifyOn(p => p.DestinationCheckIsloading).WhenModifing(p => p.DestinationCheckIsloading);
        }

        [Test]
        public void LocationViewModel_Location_CheckParentPropertyCanRaiseChanged_ChangeRaised()
        {
            var locationViewModel = CreateInterceptedLocationViewModel();

            locationViewModel.ShouldNotifyOn(p => p.Location)
                .When(p => locationViewModel.Location.ParentServer.PropertyChanged +=
                        Raise.Event<PropertyChangedEventHandler>(new PropertyChangedEventArgs("whatever")));

            locationViewModel.ShouldNotifyOn(p => p.Location)
                .When(p => locationViewModel.Location.ParentServer.ParentPlateform.PropertyChanged +=
                        Raise.Event<PropertyChangedEventHandler>(new PropertyChangedEventArgs("whatever")));

            locationViewModel.ShouldNotifyOn(p => p.Location)
                .When(p => locationViewModel.Location.ParentServer.ParentPlateform.ParentWarehouse.PropertyChanged +=
                        Raise.Event<PropertyChangedEventHandler>(new PropertyChangedEventArgs("whatever")));
        }

        [Test]
        [N.Category("Coverage")]
        public void LocationViewModel_ExplicitMemberCoverage_ForILocationViewModel_Ok()
        {
            var locationViewModel = CreateLocationViewModel();
            ILocationViewModel iLocationViewModel = locationViewModel;

            Assert.AreEqual(iLocationViewModel.CheckDestinationAccessCommand, locationViewModel.CheckDestinationAccessCommand);
            Assert.AreEqual(iLocationViewModel.CheckSourceAccessCommand, locationViewModel.CheckSourceAccessCommand);
        }

        private static LocationViewModel CreateInterceptedLocationViewModel()
        {
            var container = new UnityContainer();
            AopBoot.ConfigureForInterception(container);
            container.RegisterForInterception<LocationViewModel>(new InterceptionBehavior<PropertyChangedBehavior>());

            var location = Substitute.For<ILocation>();
            var view = Substitute.For<ILocationView>();
            var locationService = Substitute.For<ILocationService>();
            return container.Resolve<ILocationView, ILocation, ILocationService, LocationViewModel>(view, location, locationService);
        }

        private static LocationViewModel CreateLocationViewModel()
        {
            var locationService = Substitute.For<ILocationService>();

            return CreateLocationViewModel(locationService);
        }

        private static LocationViewModel CreateLocationViewModel(ILocationService locationService)
        {
            var view = Substitute.For<ILocationView>();
            var location = Substitute.For<ILocation>();

            return new LocationViewModel(view, location, locationService);
        }
    }
}
